#include "private_implementations/JsonParser.h"

namespace simframework
{
	namespace json
	{
		static const std::wstring JSON_EMPTY_KEY = L"JSON_EMPTY_KEY";

		static void jsonTrim(std::wstring::iterator& begin, std::wstring::iterator& end)
		{
			while(begin != end && isspace(*begin) )
			{
				++begin;
			}	
		}


		JsonParser::JsonParser(const std::wstring& str):jsonString_(str)
		{
			begin_ = jsonString_.begin();
			end_ = jsonString_.end();
			current_ = jsonString_.begin();
		}

		IJsonNode* JsonParser::parse()
		{
			return getValue(current_, end_, JSON_EMPTY_KEY);
		}


		IJsonNode* JsonParser::getValue(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& key)
		{
			jsonTrim(current, end);
			IJsonNode* node = NULL;
			switch (*current)
			{
			case '{':
				{
					node = getJsonObjectNode(current, end, key);
				}
				break;
			case '[':
				{
					node = getJsonArrayNode(current, end, key);
				}
				break;
			case '\"':
				{
					node = getJsonStringNode(current, end, key);
				}
				break;
			case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 
				{
					node = getJsonDoubleNode(current, end, key);
				}
				break;
			case 't': case 'f':
				{
					node = getJsonBoolNode(current, end, key);
				}
				break;
			case 'n':
				{
					node = getJsonNullNode(current, end, key);
				}
				break;
			}
			return node;	
		}

		JsonObjectNode* JsonParser::getJsonObjectNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& parentKey)
		{
			jsonTrim(current, end);
			JsonObjectNode* parent = new JsonObjectNode(parentKey);
			++current;
			for (current; current != end; ++current)
			{			
				std::wstring key = getString(current, end);
				++current;
				if (*current != ':')
				{
					throw 1;
				}
				++current;
				IJsonNode* node = getValue(current, end, key);
				parent->addNode(node);	

				if (*current == '}')
				{
					++current;
					break;
				}

				if (*current != ',')
				{
					throw 1;
				}			
			}		
			return parent;
		}
		JsonArrayNode* JsonParser::getJsonArrayNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& parentKey)
		{
			jsonTrim(current, end);
			JsonArrayNode* parent = new JsonArrayNode(parentKey);
			++current;
			for (current; current != end; ++current)
			{			
				IJsonNode* node = getValue(current, end, JSON_EMPTY_KEY);
				parent->addNode(node);

				if (*current == ']')
				{
					++current;
					break;
				}

				if (*current != ',')
				{
					throw 1;
				}
			}		
			return parent;
		}

		JsonStringNode* JsonParser::getJsonStringNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& key)
		{	
			jsonTrim(current, end);
			std::wstring strValue = getString(current, end);
			return new JsonStringNode(key, strValue);
		}

		JsonDoubleNode* JsonParser::getJsonDoubleNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& key)
		{
			jsonTrim(current, end);
			std::wstring::iterator doubleBegin = current;
			bool foundDecimalPoint = false;

			for (current; current != end; ++current)
			{
				switch (*current)
				{
				case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 
					{					
					}
					break;
				case '.':
					{
						if (!foundDecimalPoint)
						{
							foundDecimalPoint = true;
						}
						else
						{
							throw 1;
						}
					}
					break;
				default:
					{
						return new JsonDoubleNode(key, _wtof(std::wstring(doubleBegin, current).c_str()));
					}
				}
			}
			return NULL;
		}

		JsonBoolNode* JsonParser::getJsonBoolNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& key)
		{
			jsonTrim(current, end);
			std::wstring::iterator boolBegin = current;
			switch (*current)
			{
			case 'f':
				{
					std::advance(current, 5);
					if (L"false" == std::wstring(boolBegin, current))
					{
						return new JsonBoolNode(key, false);
					}
				}
				break;

			case 't':
				{
					std::advance(current, 4);
					if (L"true" == std::wstring(boolBegin, current))
					{
						return new JsonBoolNode(key, true);
					}
				}
				break;
			default:
				return NULL;
			}
			return NULL;
		}

		JsonNullNode* JsonParser::getJsonNullNode(std::wstring::iterator& current, std::wstring::iterator& end, const std::wstring& key)
		{
			jsonTrim(current, end);
			std::wstring::iterator nullBegin = current;

			std::advance(current, 4);
			if (L"null" == std::wstring(nullBegin, current))
			{
				return new JsonNullNode(key);
			}
			return NULL;
		}

		std::wstring JsonParser::getString(std::wstring::iterator& current, std::wstring::iterator& end)
		{
			jsonTrim(current, end);
			std::wstring::iterator strBegin = ++current;
			for (current; current != end; ++current)
			{
				switch (*current)
				{
				case '\"':
					{
						return std::wstring(strBegin, current);
					}
				}
			}
			throw 1;
		}
	}
}